<?php
/**
 * 微信jssdk验证类
 *
 * User: 周剑锋 <camfee@foxmail.com>
 * Date: 2017/4/24
 * Time: 16:07
 */

namespace Mobile;

use Common\Bridge;

class WeiXinJs
{
    const MC_TOKEN_KEY = 'WX_JS_TOKEN_%s'; //app_id
    const MC_TICKET_KEY = 'WX_JS_TICKET_%s'; //app_id
    const MC_EXPIRE = 7000;
    const URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin/';
    private static $js_app_id;
    private static $js_app_secret;

    /**
     * 配置加载
     */
    private static function getConfig()
    {
        $config_array = loadconf('wap/weixin');
        self::$js_app_id = $config_array['weixinjs']['JsAppId'];
        self::$js_app_secret = $config_array['weixinjs']['JsAppSecret'];
    }

    /**
     * 获取jssdk验证后的数据包
     *
     * @param string $url
     * @return array|bool
     */
    public static function getSignPackage($url = '')
    {
        self::getConfig();
        $ticket = self::getTicket();
        if (empty($ticket)) {
            return false;
        }
        // 注意 URL 一定要动态获取，不能 hardcode.
        if (empty($url)) {
            $protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? 'https://' : 'http://';
            $url = "{$protocol}{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
        }

        $timestamp = time();
        $nonceStr = self::getNonceStr();

        // 这里参数的顺序要按照 key 值 ASCII 码升序排序
        $string = "jsapi_ticket={$ticket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$url}";
        $signature = sha1($string);

        $signPackage = [
            'appId' => self::getJsAppId(),
            'nonceStr' => $nonceStr,
            'timestamp' => $timestamp,
            'url' => $url,
            'signature' => $signature,
            //            "rawString" => $string
        ];

        return $signPackage;
    }

    /**
     * 获取随机字符串
     *
     * @param int $length
     * @return string
     */
    public static function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 获取jssdk appid
     * @return mixed
     */
    public static function getJsAppId()
    {
        return self::$js_app_id;
    }

    /**
     * 获取jssdk appsecret
     * @return mixed
     */
    public static function getJsAppSecret()
    {
        return self::$js_app_secret;
    }

    /**
     * 获取AccessToken
     * @return bool|mixed
     */
    public static function getAccessToken()
    {
        $key = self::getMcTokenKey();
        $token = self::getMc()->get($key);
        if (!empty($token)) {
            return $token;
        }
        $app_id = self::getJsAppId();
        $secret = self::getJsAppSecret();
        $url = self::URL_PREFIX . "token?grant_type=client_credential&appid={$app_id}&secret={$secret}";
        $res = self::httpGet($url);
        if (empty($res)) {
            return false;
        }
        $result = json_decode($res);
        if (!isset($result->access_token)) {
            return false;
        }
        $token = $result->access_token;

        self::getMc()->set($key, $token, self::MC_EXPIRE);

        return $token;
    }

    /**
     * 获取票据
     * @return bool|mixed
     */
    private static function getTicket()
    {
        $key = self::getMcTicketKey();
        $ticket = self::getMc()->get($key);
        if (!empty($ticket)) {
            return $ticket;
        }
        $token = self::getAccessToken();
        if (empty($token)) {
            return false;
        }

        $url = self::URL_PREFIX . "ticket/getticket?type=jsapi&access_token={$token}";

        $res = self::httpGet($url);
        if (empty($res)) {
            return false;
        }
        $result = json_decode($res);
        if (empty($result->ticket)) {
            return false;
        }
        $ticket = $result->ticket;

        self::getMc()->set($key, $ticket, self::MC_EXPIRE);

        return $ticket;
    }

    /**
     * 获取缓存数据实例
     * @return \lib\plugins\cache\Memcached
     */
    private static function getMc()
    {
        return Bridge::memcache(Bridge::MEMCACHE_DEFAULT);
    }

    /**
     * 获取token缓存的key
     * @return string
     */
    private static function getMcTokenKey()
    {
        return sprintf(self::MC_TOKEN_KEY, self::getJsAppId() . '');
    }

    /**
     * 获取票据缓存的key
     * @return string
     */
    private static function getMcTicketKey()
    {
        return sprintf(self::MC_TICKET_KEY, self::getJsAppId() . '');
    }

    /**
     * http get请求函数
     * @param $url
     * @param int $second
     * @return mixed
     */
    private static function httpGet($url, $second = 20)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_TIMEOUT, $second);
        // 为保证第三方服务器与微信服务器之间数据传输的安全性，所有微信接口采用https方式调用，必须使用下面2行代码打开ssl安全校验。
        // 如果在部署过程中代码在此处验证失败，请到 http://curl.haxx.se/ca/cacert.pem 下载新的证书判别文件。
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true);
        curl_setopt($curl, CURLOPT_URL, $url);

        $res = curl_exec($curl);
        curl_close($curl);

        if (empty($res)) {
            $log = [
                'url' => $url,
                'return' => $res,
                'time' => date('Y-m-d H:i:s')
            ];
            runtime_log(__CLASS__ . '/httpGetError', $log);
        }

        return $res;
    }
}
